﻿// ———————————————————————–
// <copyright file="IPolling.cs" company="The MITRE Corporation">
//    Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
// </copyright>
// ———————————————————————–
/////////////////////////////////////////////////////////////////////////////////////////////////
// IPolling.cs - "Abstract Class For Creating A Timer To Sychrnoize Variables. 
//// Project: "IC.NETSOA"- "ICNETServices"
//
// Language:    C#, .NET 4.0
// Platform:    Windows 7, VS 2010
// Author:      Don McGarry The MITRE Corporation
/////////////////////////////////////////////////////////////////////////////////////////////////

// Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//
// NOTICE
// This software was produced for the U. S. Government
// under Contract No. FA8721-09-C-0001, and is
// subject to the Rights in Noncommercial Computer Software
// and Noncommercial Computer Software Documentation Clause
// (DFARS) 252.227-7014 (JUN 1995)
using System.Collections.Generic;
using System.Threading;
using System.Timers;
using EDXLSharp.EDXLDELib;
using log4net;

namespace ICNETServices
{
  /// <summary>
  /// Contains methods for  comparing ints in 2 asynchronous objects. 
  /// </summary>
  public abstract class IPolling
  {
    /// <summary>
    /// Notify waiting threads that an event has occured.
    /// </summary>
    private ManualResetEvent disposeEvent, timerResetEvent;

    /// <summary>
    /// Used for logging.
    /// </summary>
    private ILog log;

    /// <summary>
    /// The path to the plugin directory.
    /// </summary>
    private string plugindir;

    /// <summary>
    /// A basic timer. 
    /// </summary>
    private System.Timers.Timer timer;

    /// <summary>
    /// Points to the function that will be called when the timer expires.
    /// </summary>
    private DEDelegate decallback;
    
    /// <summary>
    /// The timer interval.
    /// </summary>
    private int pollinterval;

    /// <summary>
    /// The variable that is trying to be syncd between two threads. 
    /// </summary>
    private int syncpoint = 0;

    /// <summary>
    /// Currently not used.
    /// </summary>
    private string objectName;
  
    /// <summary>
    /// Initializes a new instance of the IPolling class. Logs a message with status fatal.
    /// </summary>
    public IPolling()
    {
      this.log.Fatal("IPolling BAD BAD Ctor");
    }

    /// <summary>
    /// Initializes a new instance of the IPolling class. 
    /// </summary>
    /// <param name="s">The plugin directory.</param>
    /// <param name="interval">The interval of the timer.</param>
    /// <param name="callback">The callback function.</param>
    /// <param name="masterlog">The log to write to.</param>
    public IPolling(string s, int interval, DEDelegate callback, ILog masterlog)
    {
      this.Log = masterlog;
      this.objectName = ((object)this).GetType().Name;

      this.Log.Info(this.objectName + @" Ctor");
      this.Plugindir = s;
      this.Pollinterval = interval;
      this.Timer = new System.Timers.Timer();
      this.Timer.Interval = this.pollinterval;
      this.Timer.Elapsed += new ElapsedEventHandler(this.TimerInternalTick);
      this.Timer.AutoReset = true;
      this.Decallback = callback;
      this.DisposeEvent = new ManualResetEvent(false);
      this.TimerResetEvent = new ManualResetEvent(false);
    }

    /// <summary>
    /// Points to the function that will be called when the timer expires.
    /// </summary>
    /// <param name="messages">The DE messages that will be passed into the callback function.</param>
    public delegate void DEDelegate(List<EDXLDE> messages, ILog log, System.Timers.Timer timer);

    /// <summary>
    /// Gets or sets Log.
    /// </summary>
    public ILog Log
    {
      get { return this.log; }
      set { this.log = value; }
    }

    /// <summary>
    /// Gets or sets TimerResetEvent.
    /// </summary>
    protected ManualResetEvent TimerResetEvent
    {
      get { return this.timerResetEvent; }
      set { this.timerResetEvent = value; }
    }

    /// <summary>
    /// Gets or sets DisposeEvent.
    /// </summary>
    protected ManualResetEvent DisposeEvent
    {
      get { return this.disposeEvent; }
      set { this.disposeEvent = value; }
    }

    /// <summary>
    /// Gets or sets Plugindir.
    /// </summary>
    protected string Plugindir
    {
      get { return this.plugindir; }
      set { this.plugindir = value; }
    }

    /// <summary>
    /// Gets or sets Timer. 
    /// </summary>
    public System.Timers.Timer Timer
    {
      get { return this.timer; }
      set { this.timer = value; }
    }

    /// <summary>
    /// Gets or sets Decallback
    /// </summary>
    protected DEDelegate Decallback
    {
      get { return this.decallback; }
      set { this.decallback = value; }
    }

    /// <summary>
    /// Gets or sets Pollinterval 
    /// </summary>
    protected int Pollinterval
    {
      get { return this.pollinterval; }
      set { this.pollinterval = value; }
    }

    /// <summary>
    /// Starts the timer.
    /// </summary>
    public void ContinueTimer()
    {
      this.Timer.Start();
    }

    /// <summary>
    /// Tells the thread that an event has ocurred and that it can be unblocked.
    /// </summary>
    public void Close()
    {
      // unblock thread
      this.DisposeEvent.Set();
    }

    /// <summary>
    /// Starts a timer, and when it expires checks a variable (of type int) shared by 2 threads. 
    /// The timer expire when the two variables are equal.
    /// </summary>
    /// <param name="threadContext">Currently not used.</param>
    public void PollEndpoint(object threadContext)
    {
      this.Timer.Start();

      // block thread
      this.Log.Info(this.objectName + @" thread blocked for close event");
      this.DisposeEvent.WaitOne();

      // Close() unblocked the thread
      this.Log.Info(this.objectName + @" received close event");

      while (Interlocked.CompareExchange(ref this.syncpoint, -1, 0) != 0)
      {
        // Give up the rest of this thread's current time
        // slice. This is a naive algorithm for yielding.
        Thread.Sleep(1);
      }

      // stop the timer event by calling the stop method which sets enabled to false
      this.Timer.Stop();

      // Thread.Sleep(500);
      this.Log.Info(this.objectName + @" Stopped");
    }

    /// <summary>
    /// Runs every timer tick.
    /// </summary>
    /// <param name="sender">The object that triggered the event.</param>
    /// <param name="e">The event that causd the tick</param>
    protected abstract void TimerTick(object sender, ElapsedEventArgs e);

    /// <summary>
    /// Runs every timer tick and tries to sync two variables (of int type) 
    /// </summary>
    /// <param name="sender">The object that triggered the event.</param>
    /// <param name="e">The event that causd the tick</param>
    private void TimerInternalTick(object sender, ElapsedEventArgs e)
    {
      int sync = Interlocked.CompareExchange(ref this.syncpoint, 1, 0);
      if (sync == 0)
      {
        this.TimerTick(sender, e);
        this.syncpoint = 0;
      }
    }
  }
}
